package com.zhufeng.gateway.db.route;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.handler.AsyncPredicate;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @ClassName: ZhufengJsonRoutePredicateFactory
 * @Description 解析json请求
 * @author 月夜烛峰
 * @date 2022/9/14 19:52
 */
@Slf4j
@Component
public class ZhufengJsonReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory<ZhufengJsonReadBodyRoutePredicateFactory.Config> {

    private static final String JSON_ATTRIBUTE = "msg_type_predicate_json_attribute";
    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedMsgTypeObject";

    private final List<HttpMessageReader<?>> messageReaders;

    public ZhufengJsonReadBodyRoutePredicateFactory() {
        super(Config.class);
        this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    }

    public ZhufengJsonReadBodyRoutePredicateFactory(List<HttpMessageReader<?>> messageReaders) {
        super(Config.class);
        this.messageReaders = messageReaders;
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("patterns");
    }

    @Override
    public AsyncPredicate<ServerWebExchange> applyAsync(Config config) {
        return new AsyncPredicate<ServerWebExchange>() {
            @Override
            public Publisher<Boolean> apply(ServerWebExchange exchange) {

                Class<JSONObject> inClass = JSONObject.class;

                Predicate<JSONObject> predicate = msgInfo -> {
                    log.info("请求数据:" + msgInfo);
                    log.info("patterns:" + JSONObject.toJSONString(config.patterns));

                    String msgtype = msgInfo.getString("msgType");
                    if (config.patterns.contains(msgtype)) {
                        log.info("验证成功");
                        return true;
                    }
                    log.error("报文格式错误");
                    return false;
                };

                Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
                log.info("打印属性cachedBody:" + cachedBody);
                if (cachedBody != null) {
                    try {
                        boolean test = predicate.test((JSONObject) cachedBody);
                        exchange.getAttributes().put(JSON_ATTRIBUTE, test);
                        return Mono.just(test);
                    } catch (ClassCastException e) {
                        log.error("Predicate test failed because class in predicate "
                                + "does not match the cached body object", e);
                    }
                    return Mono.just(false);
                } else {
                    return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
                            (serverHttpRequest) -> ServerRequest
                                    .create(exchange.mutate().request(serverHttpRequest)
                                            .build(), messageReaders)
                                    .bodyToMono(inClass)
                                    .doOnNext(objectValue -> exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
                                    .map(objectValue -> predicate.test(objectValue)));
                }
            }

            @Override
            public String toString() {
                return String.format("ReadBody: %s", String.class);
            }
        };
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        throw new UnsupportedOperationException(
                "ReadBodyPredicateFactory is only async.");
    }

    public static class Config {

        private List<String> patterns = new ArrayList<String>();


        public List<String> getPatterns() {
            return patterns;
        }

        public Config setPatterns(List<String> patterns) {
            this.patterns = patterns;
            return this;
        }

    }
}